// Copyright (c) 2020 Presto Labs Pte. Ltd.
// Author: donggu

#pragma once

// #include <charconv> // not available in gcc 7
#include <stdexcept>
#include <string>
#include <string_view>
#include <system_error>

#include <fast_double_parser.h>

#include "absl/strings/charconv.h"

/**
For both gcc and clang,
std::from_chars only support integral types
absl::from_chars only support floating types

however, fast_double_parser is always faster then absl::from_chars.
**/

// not available in gcc 7
// template <typename T>
// inline T std_from_chars(std::string_view s) {
//   // auto x = T{};
//   auto x = T{};
//   if (auto [p, ec] = std::from_chars(s.data(), s.data() + s.size(), x); ec != std::errc()) {
//     throw std::runtime_error("fail to from_chars: " + std::string(s));
//   }
//   return x;
// }

// you should not use this function except benchmark
template <typename T>
inline T absl_from_chars(std::string_view s) {
  // auto x = T{};
  auto x = T{};
  if (auto [p, ec] = absl::from_chars(s.data(), s.data() + s.size(), x); ec != std::errc()) {
    (void)p;
    throw std::runtime_error("fail to from_chars: " + std::string(s));
  }
  return x;
}

inline double stod(const char* s) { return fast_double_parser::stod(s); }
inline double stod(std::string_view s) { return fast_double_parser::stod(s.data()); }

// inline uint64_t stoull(std::string_view s) { return std_from_chars<uint64_t>(s); }

// https://jsteemann.github.io/blog/2016/06/02/fastest-string-to-uint64-conversion-method/
inline uint64_t stoull(std::string_view s) {
  uint64_t result = 0;

  size_t const length = s.size();
  switch (length) {
    case 20:
      result += (s[length - 20] - '0') * 10000000000000000000ULL;
      [[fallthrough]];
    case 19:
      result += (s[length - 19] - '0') * 1000000000000000000ULL;
      [[fallthrough]];
    case 18:
      result += (s[length - 18] - '0') * 100000000000000000ULL;
      [[fallthrough]];
    case 17:
      result += (s[length - 17] - '0') * 10000000000000000ULL;
      [[fallthrough]];
    case 16:
      result += (s[length - 16] - '0') * 1000000000000000ULL;
      [[fallthrough]];
    case 15:
      result += (s[length - 15] - '0') * 100000000000000ULL;
      [[fallthrough]];
    case 14:
      result += (s[length - 14] - '0') * 10000000000000ULL;
      [[fallthrough]];
    case 13:
      result += (s[length - 13] - '0') * 1000000000000ULL;
      [[fallthrough]];
    case 12:
      result += (s[length - 12] - '0') * 100000000000ULL;
      [[fallthrough]];
    case 11:
      result += (s[length - 11] - '0') * 10000000000ULL;
      [[fallthrough]];
    case 10:
      result += (s[length - 10] - '0') * 1000000000ULL;
      [[fallthrough]];
    case 9:
      result += (s[length - 9] - '0') * 100000000ULL;
      [[fallthrough]];
    case 8:
      result += (s[length - 8] - '0') * 10000000ULL;
      [[fallthrough]];
    case 7:
      result += (s[length - 7] - '0') * 1000000ULL;
      [[fallthrough]];
    case 6:
      result += (s[length - 6] - '0') * 100000ULL;
      [[fallthrough]];
    case 5:
      result += (s[length - 5] - '0') * 10000ULL;
      [[fallthrough]];
    case 4:
      result += (s[length - 4] - '0') * 1000ULL;
      [[fallthrough]];
    case 3:
      result += (s[length - 3] - '0') * 100ULL;
      [[fallthrough]];
    case 2:
      result += (s[length - 2] - '0') * 10ULL;
      [[fallthrough]];
    case 1:
      result += (s[length - 1] - '0');
  }
  return result;
}

// inline int64_t stoll(std::string_view s) { return std_from_chars<int64_t>(s); }
inline int64_t stoll(std::string_view s) {
  int64_t result = 0;

  bool negative = (s[0] == '-');
  if (negative || s[0] == '+') {
    s = s.substr(1);
  }

  size_t const length = s.size();
  switch (length) {
    case 20:
      result += (s[length - 20] - '0') * 10000000000000000000ULL;
      [[fallthrough]];
    case 19:
      result += (s[length - 19] - '0') * 1000000000000000000ULL;
      [[fallthrough]];
    case 18:
      result += (s[length - 18] - '0') * 100000000000000000ULL;
      [[fallthrough]];
    case 17:
      result += (s[length - 17] - '0') * 10000000000000000ULL;
      [[fallthrough]];
    case 16:
      result += (s[length - 16] - '0') * 1000000000000000ULL;
      [[fallthrough]];
    case 15:
      result += (s[length - 15] - '0') * 100000000000000ULL;
      [[fallthrough]];
    case 14:
      result += (s[length - 14] - '0') * 10000000000000ULL;
      [[fallthrough]];
    case 13:
      result += (s[length - 13] - '0') * 1000000000000ULL;
      [[fallthrough]];
    case 12:
      result += (s[length - 12] - '0') * 100000000000ULL;
      [[fallthrough]];
    case 11:
      result += (s[length - 11] - '0') * 10000000000ULL;
      [[fallthrough]];
    case 10:
      result += (s[length - 10] - '0') * 1000000000ULL;
      [[fallthrough]];
    case 9:
      result += (s[length - 9] - '0') * 100000000ULL;
      [[fallthrough]];
    case 8:
      result += (s[length - 8] - '0') * 10000000ULL;
      [[fallthrough]];
    case 7:
      result += (s[length - 7] - '0') * 1000000ULL;
      [[fallthrough]];
    case 6:
      result += (s[length - 6] - '0') * 100000ULL;
      [[fallthrough]];
    case 5:
      result += (s[length - 5] - '0') * 10000ULL;
      [[fallthrough]];
    case 4:
      result += (s[length - 4] - '0') * 1000ULL;
      [[fallthrough]];
    case 3:
      result += (s[length - 3] - '0') * 100ULL;
      [[fallthrough]];
    case 2:
      result += (s[length - 2] - '0') * 10ULL;
      [[fallthrough]];
    case 1:
      result += (s[length - 1] - '0');
  }
  if (negative) {
    return -result;
  }
  return result;
}
